﻿// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

#nullable disable

using System.Text;

namespace osu.Framework.IO.Network
{
    public static class UrlEncoding
    {
        public static string UrlEncode(string str)
        {
            if (str == null)
            {
                return null;
            }

            return UrlEncode(str, Encoding.UTF8, false);
        }

        public static string UrlEncodeParam(string str)
        {
            if (str == null)
            {
                return null;
            }

            return UrlEncode(str, Encoding.UTF8, true);
        }

        public static string UrlEncode(string str, Encoding e, bool paramEncode)
        {
            if (str == null)
            {
                return null;
            }

            return Encoding.ASCII.GetString(UrlEncodeToBytes(str, e, paramEncode));
        }

        public static byte[] UrlEncodeToBytes(string str, Encoding e, bool paramEncode)
        {
            if (str == null)
            {
                return null;
            }

            byte[] bytes = e.GetBytes(str);
            return urlEncodeBytesToBytesPublic(bytes, 0, bytes.Length, false, paramEncode);
        }

        private static byte[] urlEncodeBytesToBytesPublic(byte[] bytes, int offset, int count, bool alwaysCreateReturnValue, bool paramEncode)
        {
            int num = 0;
            int num2 = 0;

            for (int i = 0; i < count; i++)
            {
                char ch = (char)bytes[offset + i];

                if (paramEncode && ch == ' ')
                {
                    num++;
                }
                else if (!IsSafe(ch))
                {
                    num2++;
                }
            }

            if (!alwaysCreateReturnValue && num == 0 && num2 == 0)
            {
                return bytes;
            }

            byte[] buffer = new byte[count + num2 * 2];
            int num4 = 0;

            for (int j = 0; j < count; j++)
            {
                byte num6 = bytes[offset + j];
                char ch2 = (char)num6;

                if (IsSafe(ch2))
                {
                    buffer[num4++] = num6;
                }
                else if (paramEncode && ch2 == ' ')
                {
                    buffer[num4++] = 0x2b;
                }
                else
                {
                    buffer[num4++] = 0x25;
                    buffer[num4++] = (byte)IntToHex((num6 >> 4) & 15);
                    buffer[num4++] = (byte)IntToHex(num6 & 15);
                }
            }

            return buffer;
        }

        public static bool IsSafe(char ch)
        {
            if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))
            {
                return true;
            }

            switch (ch)
            {
                case '\'':
                case '(':
                case ')':
                case '*':
                case '-':
                case '.':
                case '_':
                case '!':
                    return true;
            }

            return false;
        }

        public static char IntToHex(int n)
        {
            if (n <= 9)
            {
                return (char)(n + 0x30);
            }

            return (char)(n - 10 + 0x61);
        }
    }
}
